package dan.frontend
import chisel3._
import chisel3.util._
import dan.common.CoreModule
import dan.common.CoreBundle
import dan.common.UOp
import dan.utility.FrontendUtility.rotateLeft
import dan.utility.FrontendUtility.fetchAlign
import dan.common.Consts.FPGA_RUN
import dan.utility.MaskUpper

class FetchBufIO extends CoreBundle{
    val clear = Input(Bool())
    val enq = Flipped(DecoupledIO(new FetchPack()))
    val deq = DecoupledIO(new FetchBufDeq())
}

class FetchBuf extends CoreModule{
    val io = IO(new FetchBufIO())
    val bufSize = frontendParam.fetchBufSize
    val fetchWidth = frontendParam.fetchWidth
    require(bufSize % coreWidth == 0)
    // 每周期发射corewidth个微指令为一组，buffer中包含多少组
    val deqPackNum = bufSize / coreWidth
    val buf1D = Reg(Vec(bufSize, new UOp()))
    val buf2D = Wire(Vec(deqPackNum, Vec(coreWidth, new UOp())))
    for(i <- 0 until bufSize){
        buf2D(i/coreWidth)(i%coreWidth) := buf1D(i)
    }
    // one-hot出队指针，每次出一组，只需要索引组号
    val head = RegInit(1.U(deqPackNum.W))
    // one-hot入队指针，入队是无规则的
    val tail = RegInit(1.U(bufSize.W))
    val mayFull = RegInit(false.B)
    
    def tailMeetHead(t: UInt, h: UInt, w: Int): Bool = {
        (h & VecInit(t.asBools.zipWithIndex.filter
        {case(bit, idx) => idx % w == 0}.map{case(bit, idx) => bit}).asUInt).orR
    }
    // 除了tail指针本身的位置，再入队 [1, fetchWidth-1] 个元素是否会碰到队首
    // 如果碰不到队首，则：1.至少空余6个空间 or  2.head==tail（表示队列已满）
    // 原来的代码实在是丑
    val willOverflow = (1 until fetchWidth).map(i => tailMeetHead(rotateLeft(tail, i), head, coreWidth)).reduce(_|_)           
    // 当前head == tail
    val curAtHead = tailMeetHead(tail, head, coreWidth)
    val enqEnable = !(curAtHead && mayFull || willOverflow)
    io.enq.ready := enqEnable

    val inMask = Wire(Vec(fetchWidth, Bool()))
    val inUops = Wire(Vec(fetchWidth, new UOp()))
    for(i <- 0 until fetchWidth){
        val pc = (fetchAlign(io.enq.bits.pc) + (i<<2).U) | io.enq.bits.pc(1, 0)
        inMask(i) := io.enq.valid && io.enq.bits.mask(i)
        inUops(i) := DontCare
        inUops(i).expValid := io.enq.bits.exp.valid
        inUops(i).expCause := io.enq.bits.exp.bits.trigger
        inUops(i).pcLow := pc
        inUops(i).ftqIdx := io.enq.bits.ftqIdx
        inUops(i).instr := io.enq.bits.instrPack(i)
        inUops(i).taken := io.enq.bits.cfiIdx.bits.asUInt === i.U && io.enq.bits.cfiIdx.valid
        if(!FPGA_RUN){
            inUops(i).debugInstr := io.enq.bits.instrPack(i)
            inUops(i).debugPC := pc
        }
    }
    // 入队
    // one-hot表示
    val enqIdx = Wire(Vec(fetchWidth, UInt(bufSize.W)))
    var iter = tail
    for(i <- 0 until fetchWidth){
       enqIdx(i) := iter
       iter = Mux(inMask(i), rotateLeft(iter, 1), iter)
    }
    when(enqEnable){
        tail := iter
        when(inMask.reduce(_||_)){
            mayFull := true.B
        }
    }
    for(i <- 0 until fetchWidth){
        for(j <- 0 until bufSize){
            when(enqEnable && inMask(i) && enqIdx(i)(j)){
                buf1D(j) := inUops(i)
            }
        }
    }
    
    // 出队
    // 碰到队尾说明剩余uop不足coreWidth个
    // 输出长为bufsize的向量，只有tail处可能为1，否则全0
    val mayHitTail = VecInit((0 until bufSize).map(
        // head所在的那一行为1
        idx => head(idx/coreWidth) && (!mayFull || (idx % coreWidth != 0).B)
    )).asUInt & tail

    // 输出哪一个slot可能遇到尾指针
    val slotHitTail = (0 until deqPackNum).map(
        i => mayHitTail((i+1)*coreWidth-1, i*coreWidth)
    ).reduce(_|_)   // deqPack 个 coreWidth向量按位与，输出为一个长为coreWidth的向量
    val deqHitTail = slotHitTail.orR
    // 如果目前不足一行，则不会移动头指针，因为之后会有新的op进入当前行
    val deqEnable = io.deq.ready && !deqHitTail
    when(deqEnable){
        head := rotateLeft(head, 1)
        mayFull := false.B
    }
    // 计算一个pack中可输出的掩码
    /* 
        考虑当前周期不足一个pack，正常输出，head不动；下一个周期输出一个完整的pack，存在重复指令！
        在decode阶段处理
     */
    val deqValidMask = (~MaskUpper(slotHitTail)).asBools
    (io.deq.bits.uops zip deqValidMask).map{case(op, valid) => op.valid := valid}
    (io.deq.bits.uops zip Mux1H(head, buf2D)).map{case(op, deq) => op.bits := deq}
    io.deq.valid := deqValidMask.reduce(_||_)
    when(io.clear){
        head := 1.U
        tail := 1.U
        mayFull := false.B
    }
    when(reset.asBool){
        io.deq.bits.uops map {op => op.valid := false.B}
    }
}
